/* 
 * SIT, Sistema Integral de Tramites del Ministerio de Minas y Energia
 * Copyright 2010, como se indica en  los tags @author. Revise
 * el archivo  copyright.txt en la lista de desarrollo 
 * 
 * Este NO es un software libre y no puede ser redistribuido y/o modificado
 * bajo ningun termino. El codigo es propietario y solo  puede
 * ser utilizado bajo permiso explicito del Ministerio de Minas y Energia
 * 
 * Este Software no es distribuido publicamente y solo deberia estar
 * ejecutandose en el entorno de desarrollo del Ministerio de Minas y Energia,
 * si por alguna razon este codigo se ejecuta en un entorno diferente estaria 
 * violando la ley de derechos de autor que lo proteje., Al igual que cualquier 
 * uso  del codigo o cualquiera de los componentes del sistema
 * sin autorizacion explicita de los autores.
 * 
 * Si necesita autorizacion para la utilizacion del codigo o parte de el 
 * por favor refierase a la lista de desarrollo del proyecto para contactar 
 * al autor indicado en el tag @author, o comuniquese con 
 * el grupo de arquitectura de software del proyecto.
 */
package co.edu.udistrital.database.dao.impl;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import co.edu.udistrital.database.dao.api.IFinder;
import co.edu.udistrital.database.exceptions.FinderException;

/**
 * Base Finder para operaciones de consulta sobre entidades de negocio.
 * 
 * @author <a href="mailto:rcabezas1@gmail.com">Edwin Romero</a>
 * @version $1.0
 */
@SuppressWarnings({"rawtypes","unchecked"})
@Repository
@Transactional
public class FinderImpl implements IFinder {

	/** Manejador de Entidades. */
	@PersistenceContext
	private transient EntityManager entityManager;

	/** Maximo numero de resultados para ejecutar ciertas consultas. */
	private transient int maxResults;

	private static final Logger LOG = LoggerFactory.getLogger(FinderImpl.class);

	/**
	 * Asigna el valor de maximo numero de resultados para ejecutar ciertas
	 * consultas.
	 * 
	 * @param maxResults
	 *            maximo numero de resultados para ejecutar ciertas consultas
	 */
	public void setMaxResults(int maxResults) {
		this.maxResults = maxResults;
	}

	/**
	 * Constructor base.
	 */
	public FinderImpl() {
		final int max = 500;
		maxResults = max;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see co.edu.udistrital.database.dao.api.IFinder#getMaxResults()
	 */
	@Override
	public Integer getMaxResults() {
		return Integer.valueOf(this.maxResults);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see co.edu.udistrital.database.dao.api.IFinder#findAll(java.lang.Class)
	 */
	@Override
	public List findAll(Class entityType) throws FinderException {
		return findByCriteria(entityType, -1, this.maxResults);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findById(java.io.Serializable,
	 * java.lang.Class)
	 */
	@Override
	public final Object findById(Serializable id, Class entityType)
			throws FinderException {
		try {
			Object o = entityManager.find(entityType, id);
			return o;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findById(java.io.Serializable,
	 * java.lang.Class)
	 */
	@Override
	public final Object findById(Serializable id, Class entityType,
			boolean inicializar) throws FinderException {
		try {
			Object o = findById(id, entityType);
			if (inicializar)
				this.iniatialize(o);
			return o;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByCriteria(java.lang.Class
	 * , org.hibernate.criterion.Criterion[])
	 */
	@Override
	public List findByCriteria(Class entityType, final Criterion... criterion)
			throws FinderException {
		LOG.debug("busqueda con criterios: " + criterion);
		List result = findByCriteria(entityType, -1, -1, criterion);
		return result;
	}

	public final List findByCriteria(Class entityType, final String orderBy,
			final boolean distinct, Criterion... criterion)
			throws FinderException {
		try {
			Session session = (Session) entityManager.getDelegate();

			DetachedCriteria crit = DetachedCriteria.forClass(entityType);
			for (final Criterion c : criterion) {
				if (c != null) {
					crit.add(c);
				}
			}
			if (orderBy != null && !orderBy.isEmpty()) {
				crit.addOrder(Order.asc(orderBy));
			}
			if (distinct == true) {
				crit.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);
			}
			Criteria criteria = crit.getExecutableCriteria(session);

			return criteria.list();
		} catch (Exception e) {
			throw new FinderException(e);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByCriteria(java.lang.Class
	 * , int, int, org.hibernate.criterion.Criterion[])
	 */
	@Override
	public final List findByCriteria(Class entityType, final int firstResult,
			final int maxResults, final Criterion... criterion)
			throws FinderException {
		try {
			Session session = (Session) entityManager.getDelegate();
			Criteria crit = session.createCriteria(entityType);
			for (final Criterion c : criterion) {
				if (c != null) {
					crit.add(c);
				}
			}
			if (firstResult > 0) {
				crit.setFirstResult(firstResult);
			}
			if (maxResults > 0) {
				crit.setMaxResults(maxResults);
			}
			return crit.list();
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByCriteria(java.lang.Class
	 * , int, int, java.lang.String[], org.hibernate.criterion.Criterion[])
	 */
	@Override
	public final List findByCriteria(Class entityType, final int firstResult,
			final int maxResults, final String[] alias,
			final Criterion... criterion) throws FinderException {
		try {
			Session session = (Session) entityManager.getDelegate();
			Criteria crit = session.createCriteria(entityType);
			for (final String t : alias) {
				if (t.trim().indexOf(' ') != -1) {
					String a[] = t.split(" ");
					crit.createAlias(a[0], a[1]);
				}
			}
			for (final Criterion c : criterion) {
				crit.add(c);
			}
			if (firstResult > 0) {
				crit.setFirstResult(firstResult);
			}
			if (maxResults > 0) {
				crit.setMaxResults(maxResults);
			}

			List result = crit.list();
			return result;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByCriteria(java.lang.Class
	 * , int, int, java.lang.String[], org.hibernate.criterion.Criterion[])
	 */
	@Override
	public final List findByCriteria(Class entityType, final int firstResult,
			final int maxResults, final String[] alias, boolean inicializar,
			final Criterion... criterion) throws FinderException {
		List lista = this.findByCriteria(entityType, firstResult, maxResults,
				alias, criterion);
		if (inicializar) {
			LOG.debug("INICIALIZANDO OBJETOS ....");
			for (Object obj : lista) {
				this.iniatialize(obj);
			}
		}
		return lista;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByNamedQuery(java.lang
	 * .String)
	 */
	@Override
	public List findByNamedQuery(String queryName) throws FinderException {
		try {
			Query query = entityManager.createNamedQuery(queryName);
			List result = query.getResultList();
			return result;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	@Override
	public List findByNamedQuery(String queryName, boolean inicializar)
			throws FinderException {
		try {
			Query query = entityManager.createNamedQuery(queryName);
			List resultado = query.getResultList();
			if (inicializar) {
				LOG.debug("Inicializando objetos ...");
				for (Object obj : resultado) {
					this.iniatialize(obj);
				}
			}
			return resultado;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByQuery(java.lang.String,
	 * java.lang.Object[])
	 */
	@Override
	public List findByQuery(String squery, Object... params)
			throws FinderException {
		try {
			Query query = entityManager.createQuery(squery);
			int i = 1;
			for (Object param : params) {
				if (param != null) {
					query.setParameter(i, param);
				}
				i++;
			}
			List result = query.getResultList();
			return result;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * co.edu.udistrital.database.dao.api.IFinder#findByNamedQuery(java.lang
	 * .String)
	 */
	@Override
	public List findByNamedQuery(String queryName, Object... params)
			throws FinderException {
		try {
			Query query = entityManager.createNamedQuery(queryName);//
			LOG.info("BUSQUEDA con query=" + queryName + "  : "
					+ query.toString());
			if (params != null) {
				int i = 1;
				for (Object param : params) {
					if (param != null) {
						query.setParameter(i, param);
					}
					i++;
				}
			}
			List resultado = query.getResultList();
			return resultado;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	@Override
	public List findByNamedQuery(String queryName, boolean inicializar,
			Object... params) throws FinderException {
		try {
			Query query = entityManager.createNamedQuery(queryName);
			LOG.info("BUSQUEDA con query=" + queryName + "  : "
					+ query.toString());
			if (params != null) {
				int i = 1;
				for (Object param : params) {
					if (param != null) {
						query.setParameter(i, param);
					}
					i++;
				}
			}
			List resultado = query.getResultList();
			if (inicializar) {
				LOG.debug("Inicializando objetos ...");
				for (Object obj : resultado) {
					this.iniatialize(obj);
				}
			}
			return resultado;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	public List findByNativeQuery(String queryValue, Object... params)
			throws FinderException {
		try {
			Query query = entityManager.createNativeQuery(queryValue);

			if (params != null) {
				int i = 1;
				for (Object param : params) {
					if (param != null) {
						query.setParameter(i, param);
					}
					i++;
				}
			}
			List result = query.getResultList();
			return result;
		} catch (Exception e) {
			throw new FinderException(e);
		}
	}

	@Override
	public void executeNativeQuery(String queryValue, Object... params)
			throws FinderException {
		try {
			Query query = entityManager.createNativeQuery(queryValue);
			if (params != null) {
				int i = 1;
				for (Object param : params) {
					if (param != null) {
						query.setParameter(i, param);
					}
					i++;
				}
			}
			query.executeUpdate();
		} catch (Exception e) {
			throw new FinderException(e);
		}

	}

	public void refresh(Object o) {
		entityManager.refresh(o);

	}

	public void iniatialize(Object o) {
		Hibernate.initialize(o);
	}
}
